/**
 * 文件上传钩子函数使用示例
 * @example
 * const { loading, error, data, progress, run } = useUpload<IUploadResult>(
 *   uploadUrl,
 *   {},
 *   {
 *     maxSize: 5, // 最大5MB
 *     sourceType: ['album'], // 仅支持从相册选择
 *     onProgress: (p) => console.log(`上传进度：${p}%`),
 *     onSuccess: (res) => console.log('上传成功', res),
 *     onError: (err) => console.error('上传失败', err),
 *   },
 * )
 */

/**
 * 上传文件的URL配置
 */
export const uploadFileUrl = {
  /** 用户头像上传地址 */
  USER_AVATAR: `${import.meta.env.VITE_SERVER_BASEURL}/user/avatar`,
}

/**
 * 通用文件上传函数（支持直接传入文件路径）
 * @param url 上传地址
 * @param filePath 本地文件路径
 * @param formData 额外表单数据
 * @param options 上传选项
 */
export function useFileUpload<T = string>(url: string, filePath: string, formData: Record<string, any> = {}, options: Omit<UploadOptions, 'sourceType' | 'sizeType' | 'count'> = {}) {
  return useUpload<T>(
    url,
    formData,
    {
      ...options,
      sourceType: ['album'],
      sizeType: ['original'],
    },
    filePath,
  )
}

export interface UploadOptions {
  /** 最大可选择的图片数量，默认为1 */
  count?: number
  /** 所选的图片的尺寸，original-原图，compressed-压缩图 */
  sizeType?: Array<'original' | 'compressed'>
  /** 选择图片的来源，album-相册，camera-相机 */
  sourceType?: Array<'album' | 'camera'>
  /** 文件大小限制，单位：MB */
  maxSize?: number //
  /** 上传进度回调函数 */
  onProgress?: (progress: number) => void
  /** 上传成功回调函数 */
  onSuccess?: (res: Record<string, any>) => void
  /** 上传失败回调函数 */
  onError?: (err: Error | UniApp.GeneralCallbackResult) => void
  /** 上传完成回调函数（无论成功失败） */
  onComplete?: () => void
}

/**
 * 文件上传钩子函数
 * @template T 上传成功后返回的数据类型
 * @param url 上传地址
 * @param formData 额外的表单数据
 * @param options 上传选项
 * @returns 上传状态和控制对象
 */
export function useUpload<T = string>(url: string, formData: Record<string, any> = {}, options: UploadOptions = {},
  /** 直接传入文件路径，跳过选择器 */
  directFilePath?: string) {
  /** 上传中状态 */
  const loading = ref(false)
  /** 上传错误状态 */
  const error = ref(false)
  /** 上传成功后的响应数据 */
  const data = ref<T>()
  /** 上传进度（0-100） */
  const progress = ref(0)

  /** 解构上传选项，设置默认值 */
  const {
    /** 最大可选择的图片数量 */
    count = 1,
    /** 所选的图片的尺寸 */
    sizeType = ['original', 'compressed'],
    /** 选择图片的来源 */
    sourceType = ['album', 'camera'],
    /** 文件大小限制（MB） */
    maxSize = 10,
    /** 进度回调 */
    onProgress,
    /** 成功回调 */
    onSuccess,
    /** 失败回调 */
    onError,
    /** 完成回调 */
    onComplete,
  } = options

  /**
   * 检查文件大小是否超过限制
   * @param size 文件大小（字节）
   * @returns 是否通过检查
   */
  const checkFileSize = (size: number) => {
    const sizeInMB = size / 1024 / 1024
    if (sizeInMB > maxSize) {
      uni.showToast({
        title: `文件大小不能超过${maxSize}MB`,
        icon: 'none',
      })
      return false
    }
    return true
  }
  /**
   * 触发文件选择和上传
   * 根据平台使用不同的选择器：
   * - 微信小程序使用 chooseMedia
   * - 其他平台使用 chooseImage
   */
  const run = () => {
    if (directFilePath) {
      // 直接使用传入的文件路径
      loading.value = true
      progress.value = 0
      uploadFile<T>({
        url,
        tempFilePath: directFilePath,
        formData,
        data,
        error,
        loading,
        progress,
        onProgress,
        onSuccess,
        onError,
        onComplete,
      })
      return
    }

    // #ifdef MP-WEIXIN
    // 微信小程序环境下使用 chooseMedia API
    uni.chooseMedia({
      count,
      mediaType: ['image'], // 仅支持图片类型
      sourceType,
      success: (res) => {
        const file = res.tempFiles[0]
        // 检查文件大小是否符合限制
        if (!checkFileSize(file.size))
          return

        // 开始上传
        loading.value = true
        progress.value = 0
        uploadFile<T>({
          url,
          tempFilePath: file.tempFilePath,
          formData,
          data,
          error,
          loading,
          progress,
          onProgress,
          onSuccess,
          onError,
          onComplete,
        })
      },
      fail: (err) => {
        console.error('选择媒体文件失败:', err)
        error.value = true
        onError?.(err)
      },
    })
    // #endif

    // #ifndef MP-WEIXIN
    // 非微信小程序环境下使用 chooseImage API
    uni.chooseImage({
      count,
      sizeType,
      sourceType,
      success: (res) => {
        console.log('选择图片成功:', res)

        // 开始上传
        loading.value = true
        progress.value = 0
        uploadFile<T>({
          url,
          tempFilePath: res.tempFilePaths[0],
          formData,
          data,
          error,
          loading,
          progress,
          onProgress,
          onSuccess,
          onError,
          onComplete,
        })
      },
      fail: (err) => {
        console.error('选择图片失败:', err)
        error.value = true
        onError?.(err)
      },
    })
    // #endif
  }

  return { loading, error, data, progress, run }
}

/**
 * 文件上传选项接口
 * @template T 上传成功后返回的数据类型
 */
interface UploadFileOptions<T> {
  /** 上传地址 */
  url: string
  /** 临时文件路径 */
  tempFilePath: string
  /** 额外的表单数据 */
  formData: Record<string, any>
  /** 上传成功后的响应数据 */
  data: Ref<T | undefined>
  /** 上传错误状态 */
  error: Ref<boolean>
  /** 上传中状态 */
  loading: Ref<boolean>
  /** 上传进度（0-100） */
  progress: Ref<number>
  /** 上传进度回调 */
  onProgress?: (progress: number) => void
  /** 上传成功回调 */
  onSuccess?: (res: Record<string, any>) => void
  /** 上传失败回调 */
  onError?: (err: Error | UniApp.GeneralCallbackResult) => void
  /** 上传完成回调 */
  onComplete?: () => void
}

/**
 * 执行文件上传
 * @template T 上传成功后返回的数据类型
 * @param options 上传选项
 */
function uploadFile<T>({
  url,
  tempFilePath,
  formData,
  data,
  error,
  loading,
  progress,
  onProgress,
  onSuccess,
  onError,
  onComplete,
}: UploadFileOptions<T>) {
  try {
    // 创建上传任务
    const uploadTask = uni.uploadFile({
      url,
      filePath: tempFilePath,
      name: 'file', // 文件对应的 key
      formData,
      header: {
        // H5环境下不需要手动设置Content-Type，让浏览器自动处理multipart格式
        // #ifndef H5
        'Content-Type': 'multipart/form-data',
        // #endif
      },
      // 确保文件名称合法
      success: (uploadFileRes) => {
        console.log('上传文件成功:', uploadFileRes)
        try {
          // 解析响应数据
          const { data: _data } = JSON.parse(uploadFileRes.data)
          // 上传成功
          data.value = _data as T
          onSuccess?.(_data)
        }
        catch (err) {
          // 响应解析错误
          console.error('解析上传响应失败:', err)
          error.value = true
          onError?.(new Error('上传响应解析失败'))
        }
      },
      fail: (err) => {
        // 上传请求失败
        console.error('上传文件失败:', err)
        error.value = true
        onError?.(err)
      },
      complete: () => {
        // 无论成功失败都执行
        loading.value = false
        onComplete?.()
      },
    })

    // 监听上传进度
    uploadTask.onProgressUpdate((res) => {
      progress.value = res.progress
      onProgress?.(res.progress)
    })
  }
  catch (err) {
    // 创建上传任务失败
    console.error('创建上传任务失败:', err)
    error.value = true
    loading.value = false
    onError?.(new Error('创建上传任务失败'))
  }
}

// 获取文件扩展名
export function getFileExtension(name: string): string {
  if (!name) {
    return ''
  }
  return name.split('.').pop()?.toLowerCase() || ''
}

/**
 * 文件类型枚举
 */
export enum FileType {
  /** 图片 */
  IMAGE = 'image',
  /** 视频 */
  VIDEO = 'video',
  /** 音频 */
  AUDIO = 'audio',
  /** 文档 */
  DOCUMENT = 'document',
  /** PDF文件 */
  PDF = 'pdf',
  /** 文档文件（DOC/DOCX/TXT） */
  DOC = 'doc',
  /** 表格文件（XLS/XLSX/CSV） */
  XLS = 'xls',
  /** 演示文稿文件（PPT/PPTX） */
  PPT = 'ppt',
  /** 其他文件 */
  OTHER = 'other',
}

/**
 * 判断文件类型
 * @param name 文件名或文件路径
 * @returns 文件类型枚举值
 */
export function getFileType(name: string): FileType {
  // 获取文件扩展名
  const extension = getFileExtension(name)

  // 定义各种类型的扩展名集合
  const imageExtensions = ['jpg', 'jpeg', 'png', 'gif', 'bmp', 'webp', 'svg', 'ico']
  const videoExtensions = ['mp4', 'avi', 'mov', 'wmv', 'flv', 'mpeg', 'webm', 'mkv']
  const audioExtensions = ['mp3', 'wav', 'ogg', 'flac', 'aac', 'm4a', 'wma']
  const documentExtensions = ['md', 'xml', 'json']
  const pdfExtensions = ['pdf']
  const docExtensions = ['doc', 'docx', 'txt']
  const xlsExtensions = ['xls', 'xlsx', 'csv']
  const pptExtensions = ['ppt', 'pptx']

  // 根据扩展名判断文件类型
  if (imageExtensions.includes(extension)) {
    return FileType.IMAGE
  }
  else if (videoExtensions.includes(extension)) {
    return FileType.VIDEO
  }
  else if (audioExtensions.includes(extension)) {
    return FileType.AUDIO
  }
  else if (documentExtensions.includes(extension)) {
    return FileType.DOCUMENT
  }
  else if (pdfExtensions.includes(extension)) {
    return FileType.PDF
  }
  else if (docExtensions.includes(extension)) {
    return FileType.DOC
  }
  else if (xlsExtensions.includes(extension)) {
    return FileType.XLS
  }
  else if (pptExtensions.includes(extension)) {
    return FileType.PPT
  }
  else {
    return FileType.OTHER
  }
}

/**
 * 判断是否为图片文件
 * @param name 文件名或文件路径
 * @returns 是否为图片
 */
export function isImageFile(name: string): boolean {
  return getFileType(name) === FileType.IMAGE
}

/**
 * 判断是否为视频文件
 * @param name 文件名或文件路径
 * @returns 是否为视频
 */
export function isVideoFile(name: string): boolean {
  return getFileType(name) === FileType.VIDEO
}

/**
 * 判断是否为音频文件
 * @param name 文件名或文件路径
 * @returns 是否为音频
 */
export function isAudioFile(name: string): boolean {
  return getFileType(name) === FileType.AUDIO
}

/**
 * 判断是否为文档文件
 * @param name 文件名或文件路径
 * @returns 是否为文档
 */
export function isDocumentFile(name: string): boolean {
  return getFileType(name) === FileType.DOCUMENT || getFileType(name) === FileType.PDF || getFileType(name) === FileType.DOC || getFileType(name) === FileType.XLS || getFileType(name) === FileType.PPT || getFileType(name) === FileType.OTHER
}

/**
 * 检查存储权限（仅APP平台）
 * @returns Promise<boolean> 是否有权限
 */
export function checkStoragePermission(): Promise<boolean> {
  return new Promise((resolve) => {
    // #ifdef APP-PLUS
    // APP平台检查存储权限
    plus.android.requestPermissions(
      ['android.permission.WRITE_EXTERNAL_STORAGE'],
      (result: { [key: string]: any }) => {
        if (result && result[0] && result[0].granted) {
          resolve(true)
        }
        else {
          resolve(false)
        }
      },
      () => {
        resolve(false)
      },
    )
    // #endif
    // #ifndef APP-PLUS
    // 其他平台默认有权限
    resolve(true)
    // #endif
  })
}

/**
 * 下载文件
 * @param file 文件信息
 * @returns Promise<void>
 */
export function downloadFile(file: { name: string, url: string }): Promise<void> {
  return new Promise((resolve, reject) => {
    // 检查权限
    const hasPermission = checkStoragePermission()
    if (!hasPermission) {
      uni.showModal({
        title: '权限提示',
        content: '需要存储权限才能下载文件，请在设置中开启',
        confirmText: '去设置',
        success: (res) => {
          if (res.confirm) {
            // #ifdef APP-PLUS
            // 跳转到应用设置页面
            plus.runtime.openURL('app-settings:')
            // #endif
          }
        },
      })
      return reject(new Error('没有存储权限'))
    }

    // 显示下载提示
    uni.showLoading({
      title: '下载中...',
    })

    // 开始下载
    uni.downloadFile({
      url: file.url,
      success: (downloadRes) => {
        if (downloadRes.statusCode === 200) {
          const tempFilePath = downloadRes.tempFilePath

          // #ifdef H5
          // H5环境直接下载
          const link = document.createElement('a')
          link.href = tempFilePath
          link.download = file.name
          document.body.appendChild(link)
          link.click()
          document.body.removeChild(link)
          uni.hideLoading()
          uni.showToast({
            title: '下载成功',
            icon: 'success',
          })
          resolve()
          // #endif

          // #ifdef MP-WEIXIN
          // 微信小程序保存到本地
          uni.saveFile({
            tempFilePath,
            success: (saveRes) => {
              uni.hideLoading()
              uni.showToast({
                title: '保存成功',
                icon: 'success',
              })
              resolve()
            },
            fail: (saveError) => {
              uni.hideLoading()
              console.error('保存文件失败:', saveError)
              uni.showToast({
                title: '保存失败',
                icon: 'none',
              })
              reject(new Error(`保存文件失败: ${saveError.errMsg}`))
            },
          })
          // #endif

          // #ifdef APP-PLUS
          // APP平台保存到本地
          const fileName = file.name
          const targetPath = `_downloads/${fileName}`

          plus.io.resolveLocalFileSystemURL(targetPath, () => {
            // 文件已存在
            uni.hideLoading()
            uni.showToast({
              title: '文件已存在',
              icon: 'none',
            })
            resolve()
          }, () => {
            // 文件不存在，复制过去
            plus.io.resolveLocalFileSystemURL(tempFilePath, (entry) => {
              plus.io.resolveLocalFileSystemURL('_downloads/', (targetDir) => {
                entry.copyTo(targetDir, fileName, () => {
                  uni.hideLoading()
                  uni.showToast({
                    title: '下载成功',
                    icon: 'success',
                  })
                  resolve()
                }, (copyError) => {
                  uni.hideLoading()
                  console.error('复制文件失败:', copyError)
                  uni.showToast({
                    title: '保存失败',
                    icon: 'none',
                  })
                  reject(new Error(`保存文件失败: ${copyError.message}`))
                })
              }, (dirError) => {
                uni.hideLoading()
                console.error('获取目标目录失败:', dirError)
                uni.showToast({
                  title: '保存失败',
                  icon: 'none',
                })
                reject(new Error(`获取目录失败: ${dirError.message}`))
              })
            }, (entryError) => {
              uni.hideLoading()
              console.error('获取源文件失败:', entryError)
              uni.showToast({
                title: '保存失败',
                icon: 'none',
              })
              reject(new Error(`获取文件失败: ${entryError.message}`))
            })
          })
          // #endif
        }
        else {
          uni.hideLoading()
          uni.showToast({
            title: `下载失败: ${downloadRes.statusCode}`,
            icon: 'none',
          })
          reject(new Error(`下载失败: ${downloadRes.statusCode}`))
        }
      },
      fail: (error) => {
        uni.hideLoading()
        console.error('下载文件失败:', error)
        uni.showToast({
          title: '下载失败',
          icon: 'none',
        })
        reject(new Error(`下载失败: ${error.errMsg}`))
      },
    })
  })
}
// 格式化文件大小
export function formatFileSize(bytes?: number): string {
  if (!bytes)
    return ''

  if (bytes < 1024) {
    return `${bytes}B`
  }
  else if (bytes < 1024 * 1024) {
    return `${(bytes / 1024).toFixed(1)}KB`
  }
  else if (bytes < 1024 * 1024 * 1024) {
    return `${(bytes / (1024 * 1024)).toFixed(1)}MB`
  }
  else {
    return `${(bytes / (1024 * 1024 * 1024)).toFixed(1)}GB`
  }
}
/**
 * 打开文档文件
 * @param path 文档文件路径（支持本地路径或网络路径）
 * @returns Promise<void>
 */
export function previewDocument(path: string): Promise<void> {
  return new Promise((resolve, reject) => {
    if (!path) {
      uni.showToast({
        title: '文件路径为空',
        icon: 'none',
      })
      return reject(new Error('文件路径为空'))
    }

    // 判断是否为本地路径
    const isLocalPath = path.startsWith('/') || path.indexOf('file://') === 0

    if (isLocalPath) {
      // 本地文件直接打开
      openLocalDocument(path, resolve, reject)
    }
    else {
      // 网络文件需要先下载再打开
      downloadAndOpenDocument(path, resolve, reject)
    }
  })
}

/**
 * 打开本地文档
 * @param localPath 本地文件路径
 * @param resolve Promise resolve函数
 * @param reject Promise reject函数
 */
function openLocalDocument(localPath: string, resolve: () => void, reject: (error: Error) => void): void {
  // 使用uni.openDocument打开文件
  uni.openDocument({
    filePath: localPath,
    showMenu: true,
    success: () => {
      resolve()
    },
    fail: (error) => {
      console.error('打开本地文档失败:', error)
      uni.showToast({
        title: '打开文档失败',
        icon: 'none',
      })
      reject(new Error(`打开文档失败: ${error.errMsg}`))
    },
  })
}

/**
 * 下载并打开文档
 * @param url 文档网络地址
 * @param resolve Promise resolve函数
 * @param reject Promise reject函数
 */
function downloadAndOpenDocument(url: string, resolve: () => void, reject: (error: Error) => void): void {
  // 显示加载提示
  uni.showLoading({
    title: '正在加载文档...',
  })

  // 下载文件
  uni.downloadFile({
    url,
    success: (res) => {
      // 关闭加载提示
      uni.hideLoading()

      if (res.statusCode === 200) {
        // 下载成功，打开文档
        openLocalDocument(res.tempFilePath, resolve, reject)
      }
      else {
        // 下载失败
        console.error('下载文档失败:', res)
        uni.showToast({
          title: `下载失败: ${res.statusCode}`,
          icon: 'none',
        })
        reject(new Error(`下载失败: ${res.statusCode}`))
      }
    },
    fail: (error) => {
      // 关闭加载提示
      uni.hideLoading()

      // 下载失败
      console.error('下载文档失败:', error)
      uni.showToast({
        title: '下载文档失败',
        icon: 'none',
      })
      reject(new Error(`下载文档失败: ${error.errMsg}`))
    },
  })
}
